/**
 *  @author :   JiGuangJie
 *  @date   :   2020/5/18
 *  @time   :   14:48
 *  @Email  :   530904731@qq.com
 */

type SHADER_TYPE = 'fragment' | 'vertex';

const canvas: HTMLCanvasElement = document.getElementById('canvas-frame') as HTMLCanvasElement;
const gl: WebGLRenderingContext = canvas.getContext('webgl') as WebGLRenderingContext;

export default class Main {
    private readonly _gl: WebGLRenderingContext;
    private readonly _canvasWidth: number = 0;
    private readonly _canvasHeight: number = 0;
    private _projectionMatrix!: Float32Array;
    private _modelViewMatrix!: Float32Array;

    private vertexShaderSource: string = `
        attribute vec3 vertexPos;
        uniform mat4 modelViewMatrix;
        uniform mat4 projectionMatrix;
        void main(void){
            // 返回变换并投影后的顶点数据
            gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPos, 1.0);
        }
    `;

    private fragmentShaderSource: string = `
        void main(void){
            // 返回像素颜色：永远输出白色
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
    `;

    private shaderProgram!: WebGLProgram;
    private shaderVertexPositionAttribute!: number;
    private shaderProjectionMatrixUniform!: WebGLUniformLocation;
    private shaderModelViewMatrixUniform!: WebGLUniformLocation;

    public constructor(context: WebGLRenderingContext) {
        this._gl = context;
        context.canvas.width = 500;
        context.canvas.height = 500;
        this._canvasWidth = context.canvas.width;
        this._canvasHeight = context.canvas.height;
        this.initViewPort();
        this.initMatrices();
        let square = this.createSquare();
        this.initShader(this._gl);
        this.draw(this._gl, square);
    }

    /**
     * 初始化viewport视口
     */
    public initViewPort(): void {
        this._gl.viewport(0, 0, this._canvasWidth, this._canvasHeight);
    }

    public initMatrices(): void {
        // 正方形的变换矩阵-相对于相机沿着z轴稍微后移一些
        this._modelViewMatrix = new Float32Array(
            [
                1, 0, 0, 0,
                0, 1, 0, 0,
                0, 0, 1, 0,
                0, 0, -3.333, 1
            ]);
        // 变换矩阵（45度视野）
        this._projectionMatrix = new Float32Array(
            [
                2.41421, 0, 0, 0,
                0, 2.41421, 0, 0,
                0, 0, -1.002002, -1,
                0, 0, -0.2002002, 0
            ]);
    }

    public initShader(gl: WebGLRenderingContext) {
        let fragmentShader = this.createShader(this.fragmentShaderSource, 'fragment');
        let vertexShader = this.createShader(this.vertexShaderSource, 'vertex');
        this.shaderProgram = gl.createProgram() as WebGLProgram;
        gl.attachShader(this.shaderProgram, vertexShader);
        gl.attachShader(this.shaderProgram, fragmentShader);
        gl.linkProgram(this.shaderProgram);

        this.shaderVertexPositionAttribute = gl.getAttribLocation(this.shaderProgram, 'vertexPos');
        gl.enableVertexAttribArray(this.shaderVertexPositionAttribute);

        this.shaderProjectionMatrixUniform = gl.getUniformLocation(this.shaderProgram, 'projectionMatrix') as WebGLUniformLocation;
        this.shaderModelViewMatrixUniform = gl.getUniformLocation(this.shaderProgram, 'modelViewMatrix') as WebGLUniformLocation;

        if (!gl.getProgramParameter(this.shaderProgram, gl.LINK_STATUS)) {
            alert('Could not initialise shaders');
        }
    }

    public draw(gl: WebGLRenderingContext, obj: any): void {
        // 用黑色清空背景
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);
        // 设置顶点数组
        gl.bindBuffer(gl.ARRAY_BUFFER, obj.buffer);
        // 设置着色器
        gl.useProgram(this.shaderProgram);
        // 设置着色器参数：点点坐标、投影矩阵和模型视图矩阵
        gl.vertexAttribPointer(this.shaderVertexPositionAttribute, obj.vertSize, gl.FLOAT, false, 0, 0);
        gl.uniformMatrix4fv(this.shaderProjectionMatrixUniform, false, this._projectionMatrix);
        gl.uniformMatrix4fv(this.shaderModelViewMatrixUniform, false, this._modelViewMatrix);
        // 绘制物体
        gl.drawArrays(obj.primtype, 0, obj.nVerts);
    }

    /**
     * 创建用于绘制正方形的顶点数据
     */
    public createSquare(): any {
        let vertexBuffer: WebGLBuffer;
        vertexBuffer = this._gl.createBuffer() as WebGLBuffer;
        this._gl.bindBuffer(this._gl.ARRAY_BUFFER, vertexBuffer);
        const verts: number[] = [
            .5, .5, 0.0,
            -.5, .5, 0.0,
            .5, -.5, 0.0,
            -.5, -.5, 0.0
        ];
        this._gl.bufferData(this._gl.ARRAY_BUFFER, new Float32Array(verts), this._gl.STATIC_DRAW);
        return { buffer: vertexBuffer, vertSize: 3, nVerts: 4, primtype: this._gl.TRIANGLE_STRIP };
    }

    public createShader(str: string, type: string): any {
        let shader: WebGLShader;
        if (type == "fragment") {
            shader = this._gl.createShader(this._gl.FRAGMENT_SHADER) as WebGLShader;
        } else if (type == "vertex") {
            shader = this._gl.createShader(this._gl.VERTEX_SHADER) as WebGLShader;
        } else {
            return null;
        }
        this._gl.shaderSource(shader as any, str);
        this._gl.compileShader(shader as any);

        if (!this._gl.getShaderParameter(shader as any, this._gl.COMPILE_STATUS)) {
            alert(this._gl.getShaderInfoLog(shader as any));
            return null;
        }
        return shader;
    }
};


new Main(gl);